package cn.jasonone.jame.model;

import cn.jasonone.jame.JameApplication;
import cn.jasonone.jame.basic.GameContext;
import cn.jasonone.jame.cycle.LifeCycle;
import cn.jasonone.jame.GameScene;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValueBase;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import javafx.scene.Scene;
import javafx.stage.Stage;
import lombok.Data;
import lombok.Getter;

import java.util.*;

/**
 * 默认游戏上下文
 *
 * @author Jason
 * @since 1.0.0
 */
@Data
public class DefaultGameContext<S extends LifeCycle> implements GameContext<S> {
    /**
     * 数据缓存
     */
    @Getter
    private final ObservableMap<String, Object> items = FXCollections.synchronizedObservableMap(FXCollections.observableMap(new HashMap<>()));

    private final Map<String, Map<Object, List<Runnable>>> listeners = new HashMap<>();
    private final Map<String, List<ChangeListener>> changelisteners = new HashMap<>();
    /**
     * 上下文的创建源
     */
    private S source;
    /**
     * 父上下文,如果父上下文为null,则当前上下文为顶级上下文
     */
    private GameContext<?> parent;
    /**
     * 顶级上下文
     */

    private GameContext<JameApplication> root;

    private Stage stage;

    private Scene scene;

    private GameScene gameScene;



    public DefaultGameContext(S source, GameContext<?> parent) {
        this(source, parent, parent == null ? null : parent.getRoot());
    }

    public DefaultGameContext(S source, GameContext<?> parent, GameContext<JameApplication> root) {
        this.source = source;
        this.parent = parent;
        this.root = root == null ? (GameContext<JameApplication>) this : root;
    }

    public void setGameScene(GameScene gameScene) {
        this.gameScene = gameScene;
        this.scene = gameScene;
        this.stage = (Stage) this.scene.getWindow();
    }

    public void addListener(String key,Object value,Runnable listener){
        if (!this.listeners.containsKey(key)){
            this.listeners.put(key,new HashMap<>());
        }
        if( !this.listeners.get(key).containsKey(value)){
            this.listeners.get(key).put(value,new ArrayList<>());
        }
        this.listeners.get(key).get(value).add(listener);
    }

    @Override
    public void addListener(String key, ChangeListener listener) {
        Objects.requireNonNull(key,"监听的键不能为空");
        Objects.requireNonNull(listener,"监听器不能为空");
        if(!changelisteners.containsKey(key)){
            changelisteners.put(key,new ArrayList<>());
        }
        changelisteners.get(key).add(listener);
    }

    @Override
    public synchronized void put(String key, Object value) {
        Object oldValue=items.get(key);
        items.put(key, value);
        if (listeners.containsKey(key)) {
            Map<Object, List<Runnable>> map = listeners.get(key);
            if (map.containsKey(value)) {
                List<Runnable> listener = map.get(value);
                if (listener != null) {
                    listener.forEach(Runnable::run);
                }
            }
        }
        if (changelisteners.containsKey(key)){
            List<ChangeListener> changes = changelisteners.get(key);
            changes.forEach(c-> {
                c.changed(new ObservableValueBase() {
                              @Override
                              public Object getValue() {
                                  return value;
                              }
                          },
                        oldValue,
                        value);
            });
        }
    }

    @Override
    public Object get(String key) {
        if (hasKey(key)) {
            return items.get(key);
        }
        GameContext<?> parent = getParent();
        if (parent != null) {
            return get(key);
        }
        return null;
    }

    public byte getByte(String key) {
        return get(byte.class, key);
    }

    public short getShort(String key) {
        return get(short.class, key);
    }

    public int getInt(String key) {
        return get(int.class, key);
    }

    public long getLong(String key) {
        return get(long.class, key);
    }

    public float getFloat(String key) {
        return get(float.class, key);
    }

    public double getDouble(String key) {
        return get(double.class, key);
    }

    public char getChar(String key) {
        return get(char.class, key);
    }

    public boolean getBoolean(String key) {
        return get(boolean.class, key);
    }

    @Override
    public boolean hasKey(String key) {
        return items.containsKey(key);
    }


}
